package divert

import (
	"encoding/binary"
	"fmt"
	"net"
)

type WinDivertAddress struct {
	Timestamp int64
	//uint32_t Layer:8;
	//uint32_t Event:8;
	//uint32_t Sniffed:1;
	//uint32_t Outbound:1;
	//uint32_t Loopback:1;
	//uint32_t Impostor:1;
	//uint32_t IPv6:1;
	//uint32_t IPChecksum:1;
	//uint32_t TCPChecksum:1;
	//uint32_t UDPChecksum:1;
	//uint32_t Reserved1:8;
	Reserved1 uint32
	Reserved2 uint32
}

type BaseInfo struct {
	Layer       uint8
	Event       uint8
	Sniffed     bool
	Outbound    bool
	LoopBack    bool
	Impostor    bool
	IPv6        bool
	IPChecksum  bool
	TCPChecksum bool
	UDPChecksum bool
	Reserved1   uint8
}

func (c *WinDivertAddress) Base() *BaseInfo {
	buff := make([]byte, 4)
	binary.BigEndian.PutUint32(buff, c.Reserved1)

	v := buff[2]
	_ = v

	var addr = &BaseInfo{
		Layer:       buff[0],
		Event:       buff[1],
		Sniffed:     false,
		Outbound:    false,
		LoopBack:    false,
		Impostor:    false,
		IPv6:        false,
		IPChecksum:  false,
		TCPChecksum: false,
		UDPChecksum: false,
		Reserved1:   buff[3],
	}

	return addr
}

func UInt32ToIP(intIP uint32) net.IP {
	var bytes [4]byte
	bytes[0] = byte(intIP & 0xFF)
	bytes[1] = byte((intIP >> 8) & 0xFF)
	bytes[2] = byte((intIP >> 16) & 0xFF)
	bytes[3] = byte((intIP >> 24) & 0xFF)

	return net.IPv4(bytes[3], bytes[2], bytes[1], bytes[0])
}

type WinDivertDataNetwork struct {
	WinDivertAddress
	IfIdx    uint32
	SubIfIdx uint32
}

func (c *WinDivertDataNetwork) Interface() (net.Interface, bool) {
	ins, err := net.Interfaces()
	if err != nil {
		return net.Interface{}, false
	}

	for _, in := range ins {
		if in.Index == int(c.IfIdx) {
			return in, true
		}
	}

	return net.Interface{}, false
}

// WinDivertDataFlow 连接建立关闭事件
type WinDivertDataFlow struct {
	WinDivertAddress
	EndpointId       uint64
	ParentEndpointId uint64
	ProcessId        uint32
	LocalAddr        [4]uint32
	RemoteAddr       [4]uint32
	LocalPort        uint16
	RemotePort       uint16
	Protocol         Protocol
}

func (c *WinDivertDataFlow) GetLocalAddr() net.IP {
	return UInt32ToIP(c.LocalAddr[0])
}

func (c *WinDivertDataFlow) GetRemoteAddr() net.IP {
	return UInt32ToIP(c.RemoteAddr[0])
}

func (c *WinDivertDataFlow) String() string {
	return fmt.Sprintf("[%v] [%v] %v:%v -> %v:%v",
		c.ProcessId, Protocol(c.Protocol),
		c.GetLocalAddr(), c.LocalPort,
		c.GetRemoteAddr(), c.RemotePort,
	)
}

type WinDivertDataSocket struct {
	WinDivertAddress
	Endpoint       uint64
	ParentEndpoint uint64
	ProcessId      uint32
	LocalAddr      [4]uint32
	RemoteAddr     [4]uint32
	LocalPort      uint16
	RemotePort     uint16
	Protocol       Protocol
}

func (c *WinDivertDataSocket) GetLocalAddr() net.IP {
	return UInt32ToIP(c.LocalAddr[0])
}

func (c *WinDivertDataSocket) GetRemoteAddr() net.IP {
	return UInt32ToIP(c.RemoteAddr[0])
}

type WinDivertDataReflect struct {
	WinDivertAddress
	Timestamp int64
	ProcessId uint32
	Layer     WINDIVERT_LAYER
	Flags     uint64
	Priority  int16
}
